﻿using net.xBei.Helper;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace net.xBei.Models {
    /// <summary>
    /// 表示文件大小
    /// </summary>
    public class FileSize {
        /// <summary>
        /// 字节
        /// </summary>
        public long Bytes { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string Friendly { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public FileSize() {
            Friendly = "0Byte";
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bytes"></param>
        public FileSize(long bytes) {
            Bytes = bytes;
            Friendly = this.ToString();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bytes"></param>
        public FileSize(double bytes) : this((long)bytes) { }
        /// <summary>
        /// 返回友好的文件大小
        /// </summary>
        /// <returns></returns>
        public override string ToString() {
            return Format(Bytes);
        }

        private static readonly Regex regex = new("^(\\d+(\\.\\d+){0,1})([tgmk])$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        /// <summary>
        /// 格式化
        /// </summary>
        /// <param name="Bytes"></param>
        /// <returns></returns>
        public static string Format(long Bytes) {
            const int ws = 2;
            if (Bytes >= 1099511627776d)
                return $"{(Bytes / 1099511627776d).Round(ws)}T";
            if (Bytes >= 1073741824d)
                return $"{(Bytes / 1073741824d).Round(ws)}G";
            if (Bytes >= 1048576d)
                return $"{(Bytes / 1048576d).Round(ws)}M";
            if (Bytes >= 102d)
                return $"{(Bytes / 1024d).Round(ws)}K";
            return $"{Bytes}";
        }
        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static FileSize Parse(string value) {
            if (long.TryParse(value, out var v)) {
                return new FileSize(v);
            }
            var m = regex.Match(value);
            if (m.Success == false) throw new Exception($"{value} 不可识别");
            return m.Groups[3].Value.ToUpper() switch {
                "T" => new FileSize(double.Parse(m.Groups[1].Value) * 1099511627776d),
                "G" => new FileSize(double.Parse(m.Groups[1].Value) * 1073741824d),
                "M" => new FileSize(double.Parse(m.Groups[1].Value) * 1048576d),
                "K" => new FileSize(double.Parse(m.Groups[1].Value) * 1024d),
                _ => new FileSize(double.Parse(m.Groups[1].Value)),
            };
        }
        /// <summary>
        /// 零
        /// </summary>
        public static readonly FileSize Zero = new(0);
        /// <summary>
        /// <see cref="int"/>转<see cref="FileSize"/>
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator FileSize(int d) => new(d);
        /// <summary>
        /// <see cref="long"/>转<see cref="FileSize"/>
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator FileSize(long d) => new(d);
        /// <summary>
        /// <see cref="double"/>转<see cref="FileSize"/>
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator FileSize(double d) => new(d);
        /// <summary>
        /// 重载操作符<code>-</code>
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <returns></returns>
        public static FileSize operator -(FileSize d1, FileSize d2) {
            if (d1 == null && d2 == null) return FileSize.Zero;
            if (d1 == null) return new FileSize(0 - d2.Bytes);
            if (d2 == null) return new FileSize(d1.Bytes);
            return new FileSize(d1.Bytes - d2.Bytes);
        }
        /// <summary>
        /// 重载操作符<code>-</code>
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <returns></returns>
        public static FileSize operator -(FileSize d1, double d2) {
            if (d1 == null) return new FileSize(0 - d2);
            return new FileSize(d1.Bytes - d2);
        }
        /// <summary>
        /// 重载操作符<code>+</code>
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <returns></returns>
        public static FileSize operator +(FileSize d1, FileSize d2) {
            if (d1 == null && d2 == null) return FileSize.Zero;
            if (d1 == null) return new FileSize(d2.Bytes);
            if (d2 == null) return new FileSize(d1.Bytes);
            return new FileSize(d1.Bytes + d2.Bytes);
        }
        /// <summary>
        /// 重载操作符<code>+</code>
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <returns></returns>
        public static FileSize operator +(FileSize d1, double d2) {
            if (d1 == null) return new FileSize(d2);
            return new FileSize(d1.Bytes + d2);
        }
        /// <summary>
        /// 重载操作符<code>&gt;</code>
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <returns></returns>
        public static bool operator > (FileSize d1, FileSize d2) {
            if (d1 == null && d2 == null) return false;
            if (d1 == null) return false;
            if (d2 == null) return true;
            return d1.Bytes > d2.Bytes;
        }
        /// <summary>
        /// 重载操作符<code>&lt;</code>
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <returns></returns>
        public static bool operator <(FileSize d1, FileSize d2) {
            if (d1 == null && d2 == null) return false;
            if (d1 == null) return true;
            if (d2 == null) return false;
            return d1.Bytes < d2.Bytes;
        }
    }
}
